package pl.wroc.uni.ii.puzzle.game;

import java.util.Date;
import java.util.HashMap;

import pl.wroc.uni.ii.puzzle.R;
import pl.wroc.uni.ii.puzzle.activity.PuzzleActivity;
import pl.wroc.uni.ii.puzzle.data.DatabaseAdapter;
import pl.wroc.uni.ii.puzzle.data.Score;
import pl.wroc.uni.ii.puzzle.view.GameView;
import pl.wroc.uni.ii.puzzle.view.SwipeInterface;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Vibrator;
import android.preference.PreferenceManager;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;

public class Game implements OnSharedPreferenceChangeListener, SwipeInterface {

	public static final int START_LEVEL = -1;
	public static final int MAX_LEVEL = 9;
	public static final int CLICK_SOUND_ID = 1;
	public static final int LEVEL_COMPLETE_SOUND_ID = 2;
	public static final String PUZZLE_WS_ADDRESS = "http://puzzle-scores.appspot.com/scores/123";
	public static final long DURATION_OF_VIBRATE_NOTIFICATION = 20;
	public static final int ROWS_COUNT_EASY = 5;
	public static final int COLUMNS_COUNT_EASY = 5;
	public static final int ROWS_COUNT_MEDIUM = 10;
	public static final int COLUMNS_COUNT_MEDIUM = 5;
	public static final int ROWS_COUNT_HARD = 10;
	public static final int COLUMNS_COUNT_HARD = 10;
	
	private int screenWidth;
	private int screenHeight;
	private Context context;
	private Board board;
	private int currentLevel;
	private String hardship;
	private long finishTimeMillis;
	private long startTimeMillis;
	private Vibrator vibrator;
	private boolean soundNotificationEnabled;
	private boolean vibrationNotificationEnabled;
	private SoundPool soundPool;
	private AudioManager audioManager;
	private HashMap<Integer, Integer> soundPoolMap;
	
	public Game(Context context, String hardship) {
		this.context = context;
		this.hardship = hardship;
		currentLevel = Game.START_LEVEL;
		registerPreferenceChangeListener();
		setPreferences();
	}
	
	public void setPreferences() {
		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);

		Boolean defaultValue = false;
		vibrationNotificationEnabled = prefs.getBoolean("VIBRATION_CHECK_BOX", defaultValue);
		vibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
		
		((Activity)context).setVolumeControlStream(AudioManager.STREAM_MUSIC);
		audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
		soundNotificationEnabled = prefs.getBoolean("SOUND_CHECK_BOX", defaultValue);
		soundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 0);
	    soundPoolMap = new HashMap<Integer,Integer>();
	    soundPoolMap.put(Game.CLICK_SOUND_ID, soundPool.load(context, R.raw.click, 1));
	    soundPoolMap.put(Game.LEVEL_COMPLETE_SOUND_ID, soundPool.load(context, R.raw.level_complete, 1));
	}

	public void startNewLevel() {
		currentLevel++;
		if (currentLevel > Game.MAX_LEVEL) {
			finish();
		}
		else if (currentLevel % 2 == 0) {
			board = new PlayBoard(this, currentLevel, screenWidth, screenHeight, hardship);
		}
		else {
			board = new FlashBoard(this,currentLevel);
		}
	}

	private void finish() {
		finishTimeMillis = System.currentTimeMillis();
		Long timeSpanMillis = finishTimeMillis - startTimeMillis;
		Score score = new Score(1L,timeSpanMillis, "user", new Date(), hardship);
		persistScore(score);
		PuzzleActivity puzzle = (PuzzleActivity) context;
		puzzle.endGameWithScore(score);
	}

	private void persistScore(Score score) {
		try {
			DatabaseAdapter databaseAdapter = new DatabaseAdapter(context);
			databaseAdapter.open();
			databaseAdapter.insertHighScore(score);
			databaseAdapter.close();
		} catch (Exception e) {
			Log.e(GameView.class.getName(), "can't persist score", e);
		}
	}

	public void start() {
		final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
		screenWidth = metrics.widthPixels;
	    screenHeight = metrics.heightPixels;
		startNewLevel();
		startTimeMillis = System.currentTimeMillis();
	}

	private boolean isSoundNotificationEnabled() {
		return soundNotificationEnabled;
	}

	private boolean isVibrationNotificationEnabled() {
		return vibrationNotificationEnabled;
	}
	
	public void playSound(int soundId) {
		if (isSoundNotificationEnabled()) {
			float streamVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
			streamVolume = streamVolume / audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
			soundPool.play(soundPoolMap.get(soundId), streamVolume, streamVolume, 1, 0, 1f);
		}
	}
	
	public void vibrate(long duration) {
		if (isVibrationNotificationEnabled()) {
			vibrator.vibrate(duration);
		}
	}
	
	public void draw(Canvas canvas) {
		board.draw(canvas);
	}
	
	public Resources getResources() {
		return context.getResources();
	}
	
	private void registerPreferenceChangeListener() {
		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
		prefs.registerOnSharedPreferenceChangeListener(this);
	}
	
	@Override
	public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
		setPreferences();
	}
		
		
	@Override
	public void onRightToLeftSwipe(View v, float downX, float downY) {
		board.swipeRightToLeftCellFromCoordinates(downX, downY);
		v.invalidate();
		board.refreshState();
    }

	@Override
	public void onLeftToRightSwipe(View v, float downX, float downY) {
		board.swipeLeftToRightCellFromCoordinates(downX, downY);
		v.invalidate();
		board.refreshState();
    }

	@Override
	public void onTopToBottomSwipe(View v, float downX, float downY) {
		board.swipeTopToBottomCellFromCoordinates(downX, downY);
		v.invalidate();
		board.refreshState();
    }

	@Override
	public void onBottomToTopSwipe(View v, float downX, float downY) {
		board.swipeBottomToTopCellFromCoordinates(downX, downY);
		v.invalidate();
		board.refreshState();
    }
	
	@Override
	public void onClick(View v, float downX, float downY) {
		board.clickCellFromCoordinates(downX, downY);
		v.invalidate();
		board.refreshState();
	}

	public int getScreenWidth() {
		return screenWidth;
	}

	public int getScreenHeight() {
		return screenHeight;
	}
}
